import os
import sys
import re

from configmod import misc
from configmod import generic
import paths as routerconf

ospf_sections_regexp = re.compile(r"""
    \s*
    (
        hostname|
        password|
        enable[ ]password|
        log[ ]file|
        log[ ]record-priority|
        interface[ ]\w+|
        ip[ ]route[ ]\w+|
        access-list[ ].*|
        access-class[ ].*|
        ip[ ]forwarding|
        line[ ]\w+|
        router[ ]ospf
    )\s*(.*)\s*$""", re.VERBOSE)

ospf_variables_regexp = re.compile(r"""
    \s*
    (
        ospf[ ]router-id |
        ip[ ]ospf[ ]cost |
        redistribute |
        default-information |
        [^\s]+
    )\s*(.*?)\s*$""", re.VERBOSE)


def strip_tail(lst):
    for index, line in enumerate(lst[::-1]):
        if line.strip():
            return lst[:len(lst)-index]
    return lst

def flatten(lstlst):
    return [x for lst in lstlst for x in lst]

def get_interfaces(config, itypes):
    def enabled(itype):
        return [iface for iface in config["interfaces"][itype]
            if config["interfaces"][itype][iface]["state"]]
    return list(sorted(flatten(enabled(itype) for itype in itypes)))    

def network_routes(hostname, global_config):
    """Set network routes (static / ospf)"""
    config = global_config["routes"]
    run = []
    modified_files = []
    static_routes_fn = routerconf.filenames["static-routes"]
        
    # Static routing
    
    # /etc/network/nat-static-routes
    maxroutes = 100
    static_routes = (config["static_routes"] + 
        [None] * (maxroutes - len(config["static_routes"])))
    variables = [
        ("DEFAULT_GW", config["default_gw"]),
        ("DEFAULT_NAT", config["default_nat"]),
    ] + [("ROUTE%d" % (n+1), value) for (n, value) in enumerate(static_routes)]
#        variables = [(k.upper(), v) for (k, v) in 
#            generic.get_items(config.items(), "static_", "static_")]
    new_content = generic.update_variables_plain(
        generic.readlines(static_routes_fn), variables,
        assignation='\s*([^=]+)="?(.*?)"?\s*$', output_format='%s="%s"')
    if generic.overwrite_file(static_routes_fn, new_content):
        run.append(["/etc/init.d/networking", "restart"])
        modified_files.append(static_routes_fn)
                 
    # Dynamic routing
    
    # /etc/quagga/daemons   
    dynamic = (config["routing_mode"] == "dynamic")
    
    yesno = {True: "yes", False: "no"}
    variables = [
        ("zebra", yesno[dynamic]),
        ("ospfd", yesno[dynamic]),
    ]

    new_content = generic.update_variables_plain(
        generic.readlines(routerconf.filenames["ospf-daemon"]), variables,
        assignation='\s*([^=]+)="?(.*?)"?\s*$', output_format='%s=%s')
    if generic.overwrite_file(routerconf.filenames["ospf-daemon"], new_content):
        run.append(["/etc/init.d/quagga", "restart"])
        modified_files.append(routerconf.filenames["ospf-daemon"])            

    # /etc/quagga/zebra.conf
    interface_default = [
        ("bandwidth", "10000000"),
        ("ipv6", "nd suppress-ra"),
    ]

    variables = [
        ("hostname %s" % hostname, []),
        ("password %s" % config["dynamic_password"], []),
        ("enable password %s" % config["dynamic_password"], []),
    ]
    
    lines = generic.readlines(routerconf.filenames["ospf-zebra"])
    
    # Zebra interfaces
    enabled_interfaces = get_interfaces(global_config, 
      ["ethernet", "wireless", "bridging"])
    dynamic_interfaces = [iface for iface in config["dynamic_interfaces"]
      if iface in enabled_interfaces]
        
    assignation2_regexp = '\s*([^\s]+)\s*(.*?)\s*$'
    existing_interfaces = [value for key, value 
        in process_options(lines, assignation2_regexp) if key == "interface"]
    
    for iface in sorted(set(existing_interfaces).union(set(dynamic_interfaces))):
        options = (iface in dynamic_interfaces) and interface_default or None 
        variables.append(("interface %s" % iface, options))
        
    # Zebra static routes
    lines = strip_tail([line for line in lines if not line.startswith("ip route ")])
    for route in config["dynamic_static_routes"]:
        variables.append(("ip route %s" % route, []))
    #variables.append(("ip route", config["dynamic_static_routes"]))            
    new_content = generic.update_variables_with_sections(
        lines, variables,
        "^\s*(hostname|password|enable password|log file|log record-priority|interface \w+|ip route|access-list .*|access-class .*|ip forwarding|line \w+)\s*(.*)\s*$",
        '^\s*([^\s]+) (.*?)\s*$', "%s %s", " ")
    if generic.overwrite_file(routerconf.filenames["ospf-zebra"], new_content):
        run.append(["/etc/init.d/quagga", "restart"])
        modified_files.append(routerconf.filenames["ospf-zebra"])

    # /etc/quagga/ospfd.conf
    lines = generic.readlines(routerconf.filenames["ospf-ospfd"])

    if config["dynamic_redistribute_static"]: 
        redistribute = "static %s" % config["dynamic_redistribute_static_metric"] 
    else: redistribute = None

    if config["dynamic_default_information"] == 'normal': 
        information = "originate %s" % config["dynamic_default_information_metric"] 
    elif config["dynamic_default_information"] == 'always': 
        information = "originate always %s" % config["dynamic_default_information_metric"] 
    else: information = None
         
    variables = [
        ("hostname %s" % hostname, []),
        ("password %s" % config["dynamic_password"], []),
        ("enable password %s" % config["dynamic_password"], []),
        ("router ospf", [
            ("ospf router-id", config["dynamic_ospf_routerid"]),
            ("network", config["dynamic_ospf_routes"]), 
            ("area", config["dynamic_ospf_area"]),
            ("redistribute", redistribute),
            ("default-information", information),
        ]),
    ]
    
    interface_default = [
        ("ip ospf cost", "10"),
    ]
    
    existing_interfaces = [value for key, value 
        in process_options(lines, assignation2_regexp) if key == "interface"]
    
    for iface in sorted(set(existing_interfaces).union(set(dynamic_interfaces))):
        options = (iface in dynamic_interfaces) and interface_default or None 
        variables.append(("interface %s" % iface, options))

    new_content = generic.update_variables_with_sections(
        lines, variables,
        ospf_sections_regexp,
        ospf_variables_regexp,
        "%s %s", 
        " ")
    if generic.overwrite_file(routerconf.filenames["ospf-ospfd"], new_content):
        run.append(["/etc/init.d/quagga", "restart"])
        modified_files.append(routerconf.filenames["ospf-ospfd"])

    return modified_files, list(misc.uniq(run))

def process_options(it, regexp):
    for s in it:
        match = re.match(regexp, s)
        if not match:
            continue
        key, value = match.groups()
        yield key.strip(), value.strip()

def network_routes_get(config):
    assignation_regexp = '\s*(\w+)\s*=\s*[\'"]?(.*?)[\'"]?\s*$'

    # Get routing mode
    lines = generic.readlines(routerconf.filenames["ospf-daemon"])
    daemons = dict((key, value) for key, value 
        in process_options(lines, assignation_regexp))
    if daemons["ospfd"] == "yes" or daemons["zebra"] == "yes":
        config["routing_mode"] = "dynamic"
    else: 
        config["routing_mode"] = "static"

    # Static routes
    lines = generic.readlines(routerconf.filenames["static-routes"])
    static_routes = []
    for key, value in process_options(lines, assignation_regexp):
        if key == "DEFAULT_GW":
            config["default_gw"] = value
        elif key == "DEFAULT_NAT":
            config["default_nat"] = value
        elif key.startswith("ROUTE"):
            static_routes.append(value)
    config["static_routes"] = static_routes
    
    # zebra.conf

    assignation2_regexp = '\s*([^\s]+)\s*(.*?)\s*$'
    lines = generic.readlines(routerconf.filenames["ospf-zebra"])
    dynamic_static_routes = []
    interfaces = []
    for key, value in process_options(lines, assignation2_regexp):
        if key == "ip" and value.startswith("route "):
            route = " ".join(value.split()[1:])
            dynamic_static_routes.append(route)
        elif key == "password":
            config["dynamic_password"] = value
        elif key == "interface":
            interfaces.append(value) 
    config["dynamic_static_routes"] = dynamic_static_routes    
    config["dynamic_interfaces"] = interfaces                

    # ospfd.conf

    lines = generic.readlines(routerconf.filenames["ospf-ospfd"])
    ospf_routes = []
    areas = []
    config['dynamic_redistribute_static'] = False
    config['dynamic_default_information'] = "disabled"
    for key, value in process_options(lines, assignation2_regexp):
        if key == "network":            
            ospf_routes.append(value)
        elif key == "area":            
            areas.append(value)
        elif key == "ospf router-id":
            config["dynamic_ospf_routerid"] = value
        elif key == 'redistribute' and value.startswith("static "):
            config['dynamic_redistribute_static'] = True
            value2 = " ".join(value.split()[1:])
            config['dynamic_redistribute_static_metric'] = value2
        elif key == 'default-information':
            if value.startswith("originate always"):
                value2 = " ".join(value.split()[2:])
                config['dynamic_default_information'] = "always"
                config['dynamic_default_information_metric'] = value2
            else: 
                value2 = " ".join(value.split()[1:])
                config['dynamic_default_information'] = "normal"
                config['dynamic_default_information_metric'] = value2
    config["dynamic_ospf_routes"] = ospf_routes    
    config["dynamic_ospf_area"] = areas
